home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / octa209s.zip / octave-2.09 / libs / kpathsea / db.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  9KB  |  270 lines

  1. /* db.c: an external database to avoid filesystem lookups.
  2.  
  3. Copyright (C) 1994 Karl Berry.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.  */
  18.  
  19. #include <kpathsea/config.h>
  20.  
  21. #include <kpathsea/c-fopen.h>
  22. #include <kpathsea/c-pathch.h>
  23. #include <kpathsea/db.h>
  24. #include <kpathsea/hash.h>
  25. #include <kpathsea/line.h>
  26. #include <kpathsea/readable.h>
  27. #include <kpathsea/str-list.h>
  28. #include <kpathsea/variable.h>
  29.  
  30. /* See comments in `read_files' in cnf.c.  */
  31. string kpse_db_dir = NULL;
  32.  
  33. /* The hash table for ls-R.  */
  34. static hash_table_type db;
  35.  
  36.  
  37. /* If no DB_FILE, return false (maybe they aren't using this feature).
  38.    Otherwise, build the db and return true.  */
  39.  
  40. static boolean
  41. db_build P1C(hash_table_type *, table)
  42. {
  43.   string line;
  44.   unsigned dir_count = 0, file_count = 0; /* for debugging */
  45.   string cur_dir = NULL; /* First thing in ls-R might be a filename.  */
  46.   string db_filename = concat3 (kpse_db_dir, DIR_SEP_STRING, KPSE_DB_NAME);
  47.   FILE *db_file = fopen (db_filename, FOPEN_R_MODE);
  48.   
  49.   if (db_file)
  50.     {
  51.       while ((line = read_line (db_file)) != NULL)
  52.         {
  53.           unsigned len = strlen (line);
  54.           /* A line like `/foo:' = new dir foo */
  55.           if (IS_DIR_SEP (line[0]) && line[len - 1] == ':')
  56.             {
  57.               cur_dir = xstrdup (line);
  58.               cur_dir[len - 1] = DIR_SEP;
  59.               dir_count++;
  60.             }
  61.           else if (line[0] != 0 && cur_dir) /* other nonblank line */
  62.             { /* New hash table entry with a key of `line' and a data of
  63.                  `cur_dir'.  Already-existing identical keys are ok, since
  64.                  a file named `foo' can be in more than one directory.
  65.                  Since it doesn't hurt, share the directory name string
  66.                  among all the files in the directory. */
  67.               hash_insert (table, xstrdup (line), cur_dir);
  68.               file_count++;
  69.             }
  70.           /* else ignore blank lines */
  71.  
  72.           free (line);
  73.         }
  74.       xfclose (db_file, db_filename);
  75.       
  76.       if (file_count == 0)
  77.         {
  78.           fprintf (stderr, "kpathsea: no usable entries in %s; see the\n", 
  79.                    db_filename);
  80.           fprintf (stderr, "kpathsea: manual for how to generate ls-R.\n");
  81.         }
  82.  
  83. #ifdef DEBUG
  84.       if (KPSE_DEBUG_P (KPSE_DEBUG_HASH))
  85.         {
  86.           /* Don't make this a debugging bit, since the output is so
  87.              voluminous, and being able to specify -1 is too useful.
  88.              Instead, let people who want it run the program under
  89.              a debugger and change the variable that way.  */
  90.           boolean print_hash_table = false;
  91.  
  92.           DEBUGF3 ("%u entries (in %d directories) read from %s.\n",
  93.                    file_count, dir_count, db_filename);
  94.           if (print_hash_table)
  95.             {
  96.               DEBUGF ("Hash table built from ls-R:");
  97.               hash_print (*table);
  98.             }
  99.           fflush (stderr);
  100.         }
  101. #endif
  102.     }
  103.  
  104.   free (db_filename);
  105.   return db_file != NULL;
  106. }
  107.  
  108.  
  109. /* Insert FNAME into the hash table.  This is for files that get built
  110.    during a run.  We wouldn't want to reread all of ls-R, even if it got
  111.    rebuilt.  */
  112.  
  113. void
  114. db_insert P1C(const_string, passed_fname)
  115. {
  116.   /* We should always have called `kpse_db_search' before this.  */
  117.   assert (db.size > 0);
  118.   
  119.   /* But we might not have found ls-R; in that case, we'll have cleared
  120.      the buckets (but left the size).  */
  121.   if (db.buckets)
  122.     {
  123.       const_string dir_part;
  124.       string fname = xstrdup (passed_fname);
  125.       string baseptr = (string) basename (fname);
  126.       const_string file_part = xstrdup (baseptr);
  127.       
  128.       *baseptr = '\0';  /* Chop off the filename.  */
  129.       dir_part = fname; /* That leaves the dir, with the trailing /.  */
  130.  
  131.       hash_insert (&db, file_part, dir_part);
  132.     }
  133. }
  134.  
  135. /* Return true if FILENAME could be in PATH_ELT, i.e., if the directory
  136.    part of FILENAME matches PATH_ELT.  Have to consider // wildcards, but
  137.    $ and ~ expansion have already been done.  */
  138.      
  139. static boolean
  140. match P2C(const_string, filename,  const_string, path_elt)
  141. {
  142.   const_string original_filename = filename;
  143.   boolean matched = false;
  144.   boolean done = false;
  145.   
  146.   for (; !done && *filename && *path_elt; filename++, path_elt++)
  147.     {
  148.       if (*filename == *path_elt) /* normal character match */
  149.         ;
  150.  
  151.       else if (IS_DIR_SEP (*path_elt)  /* at // */
  152.                && original_filename < filename && IS_DIR_SEP (path_elt[-1]))
  153.         {
  154.           path_elt++; /* get past second / */
  155.           if (*path_elt == 0)
  156.             { /* Have a trailing //, which matches anything. We
  157.                  could make this part of the other case, but it seems
  158.                  pointless to do the extra work.  */
  159.               matched = true;
  160.               done = true;
  161.             }
  162.           else
  163.             { /* intermediate //, have to match rest of PATH_ELT */
  164.               for (; !matched && *filename; filename++)
  165.                 { /* Try matching at each possible character.  */
  166.                   if (*filename == *path_elt)
  167.                     matched = match (filename, path_elt);
  168.                 }
  169.             }
  170.         }
  171.       
  172.       else /* normal character nonmatch, quit */
  173.         done = true;
  174.     }
  175.   
  176.   /* If we've reached the end of PATH_ELT, and we're at the last
  177.      component of FILENAME, we've matched.  */
  178.   if (!matched && *path_elt == 0 && IS_DIR_SEP (*filename))
  179.     {
  180.       filename++;
  181.       while (*filename && !IS_DIR_SEP (*filename))
  182.         filename++;
  183.       matched = *filename == 0;
  184.     }
  185.  
  186.   return matched;
  187. }
  188.  
  189. /* Don't bother implementing a search path for the database itself.  We
  190.    get multiple databases, sort of, with the $TEXMF value for DB_DIR.  */
  191.  
  192. str_list_type *
  193. kpse_db_search P3C(const_string, name,  const_string, orig_path_elt,
  194.                    boolean, all)
  195. {
  196.   string *db_dirs, *orig_dirs;
  197.   const_string last_slash;
  198.   string path_elt;
  199.   boolean done;
  200.   str_list_type *ret;
  201.   
  202.   /* Hash up the database if this is the first call.  */
  203.   if (db.size == 0)
  204.     {
  205.       db = hash_create (7603); /* What the heck, sparse is ok.  */
  206.       if (!db_build (&db))
  207.         { /* If db can't be built, leave `size' nonzero (so we don't
  208.              rebuild it), but clear `buckets' (so we don't look in it).  */
  209.           free (db.buckets);
  210.           db.buckets = NULL;
  211.         }
  212.     }
  213.   
  214.   /* If we failed to build the database, quit.  */
  215.   if (db.buckets == NULL)
  216.     return NULL;
  217.   
  218.   /* When tex-glyph.c calls us looking for, e.g., dpi600/cmr10.pk, we
  219.      won't find it unless we change NAME to just `cmr10.pk' and append
  220.      `/dpi600' to PATH_ELT.  We are justified in using a literal `/'
  221.      here, since that's what tex-glyph.c unconditionally uses in
  222.      DPI_BITMAP_SPEC.  But don't do anything if the / begins NAME; that
  223.      should never happen.  */
  224.   last_slash = strrchr (name, '/');
  225.   if (last_slash && last_slash != name)
  226.     {
  227.       unsigned len = last_slash - name + 1;
  228.       string dir_part = xmalloc (len);
  229.       strncpy (dir_part, name, len - 1);
  230.       dir_part[len - 1] = 0;
  231.       path_elt = concat3 (orig_path_elt, "/", dir_part);
  232.       name = last_slash + 1;
  233.     }
  234.   else
  235.     path_elt = (string) orig_path_elt;
  236.  
  237.   /* We have a db.  Look up NAME.  */
  238.   orig_dirs = db_dirs = hash_lookup (db, name);
  239.  
  240.   done = false;
  241.   ret = XTALLOC1 (str_list_type);
  242.   *ret = str_list_init ();
  243.   
  244.   /* For each filename found, see if it matches the path element.  For
  245.      example, if we have ../cx/cmr10.300pk and .../ricoh/cmr10.300pk,
  246.      and the path looks like .../cx, we don't want the ricoh file.  */
  247.   while (!done && db_dirs && *db_dirs)
  248.     {
  249.       string db_file = concat (*db_dirs, name);
  250.       
  251.       if (match (db_file, path_elt) && kpse_readable_file (db_file))
  252.         {
  253.           str_list_add (ret, db_file);
  254.           if (!all) done = true;
  255.         }
  256.       else
  257.         free (db_file);
  258.       
  259.       /* On to the next directory, if any.  */
  260.       db_dirs++;
  261.     }
  262.  
  263.   /* This is just the space for the pointers, not the strings.  */
  264.   if (orig_dirs && *orig_dirs) free (orig_dirs);
  265.   
  266.   /* If we had to break up NAME, free the temporary PATH_ELT.  */
  267.   if (path_elt != orig_path_elt) free (path_elt);
  268.   return ret;
  269. }
  270.